Go to Excercises / Playground

DOM munipulation
John Duckett Book

The terms elements and element nodes are used interchangeably but when people say the DOM is working with an element, it is actually working with a node that represents that element

Access Elements

Select Individual Element Node

Document.getElementById(elementId: string): HTMLElement

// by CSS selector
ParentNode.querySelector<Element>(selectors: string): Element

Select Multiple Elements NODELIST

Document.getElementsByClassName(classNames: string): HTMLCollectionOf<Element>

Document.getElementsByTagName(TagName: string): HTMLCollectionOf<Element>

// by CSS selector
ParentNode.querySelectorAll<Element>(selectors: string): NodeListOf<Element>

Note: A NODELIST is not a array but instead is a collection so it has a length property and array like ordered index numbers

Note: You can use the index number just like array [index : number] the less prefered way is by using .item(index: number) method

Live vs Static Nodelists

Live NodeList : when script updates the nodes they are reflected in the collection the methods beginning with getElementBy* return live nodelists also typically faster

Static NodeList : script updates not reflected in the nodes methods beginning with querySelector* return static nodelists

Traversing Between Element Nodes

parentNode previousSibling / nextSibling firstChild / last Child
.

graph TB; ul{< ul > }---1li(1st < li >) 1li==<code>parentNode</code>==>ul 1li==<code>nextSibling</code>==>2li ul---2li(2nd < li >) ul---3li(3rd < li >) ul---4li(4th < li >) ul==<code>lastChild</code>==>4li

WhiteSpace Nodes

Some browsers add text node whenever they come across whitespace between elements

this is why libraries such as Jquery became popular to deal with this complication

graph TB; ul---1ws[ ] ul{< ul > }---1li(1st < li >) ul---2ws[ ] ul---2li(2nd < li >) ul---3ws[ ] ul---3li(3rd < li >) ul---4ws[ ] ul---4li(4th < li >) ul---5ws[ ] classDef orange fill:#f96 class 1ws,2ws,3ws,4ws,5ws orange

Working with Elements

graph TB; di{Element<hr>< li > }-->os(text<hr><code>nodeValue</code><br><br>Properties to access text content<br><code>innerHTML</code><br><code>textContent</code><br><br> node munipulation<br><code>createElement</code><br><code>createTextNode</code><br><code>appendChild / removeChild</code>) di-->a[attribute<hr><code>nodeValue</code><br><br><code>className / id</code> <br><code>hasAttribute</code> <br><code>getAttribute</code> <br><code>setAttribute</code> <br><code>removeAttribute</code>]

Go to Excercise

Events
John Duckett Book

Event Description
UI Events
load web page has finished loading
unload web page is unloading
error browser encounters a js error or an asset doesn't exist
resize browser window has been resized
scroll user has scrolled up or down the page
KeyBoard Events
keydown user first presses a key repeats while a key is depressed
keyup user releases a key
keypress character is being inserted repeats while a key is depressed
Mouse Events
click user presses and releases a button over the same element
dblclick user presses and release a button twice over the same element
mousedown user presses a mouse button while over an element
mouseup user releases a mouse button while over an element
mousemove user moves the mouse not on a touchscreen
mouseover user moves the mouse over an element not on a touchscreen
mouseout user moves the mouse off an element not on a touchscreen

preferable technique is to use CSS eg :hover pseudo-class
mousedown/up are separate for drag and drop functionality

Focus Events
focus / focusin elements gains a focus
blur / focusout element loses focus
Form Events
input Value in any <input> or <textarea> elemtn has changed or any element with the contenteditable attribute
change value in select box, checkbox, or radio button changes
submit user submits a form using a button or a key
reset user clicks on a form's reset button rarely used these days
cut user cuts content from a form field
copy user coppies content from a form field
paste user pastes a content into a form field
select user selects some text in a form field
Mutation Events
DOMSubtreeModified Change has been made to document
DOMNodeInserted Node has been inserted as a direct child of another node
DOMNodeRemoved Node has been removed from a another node
DOMNodeInsertedIntoDocument Node has been inserted as a descendeant of another node
DOMNodeRemovedFromDocument Node has been removed as a descended of another node

Event Binding

There are 3 major types of event handlers

HTML Event Handler

This is bad practice and out dated but may still arise in legacy code
<a onclick="hide()">

This method is bad becuase it doesn't seperate javascript code from the html

Traditional Event Handler

Unlike with html we attach using only javascript

element.onEvent = functionName

Event Listeners

element.addEventListener(event,functionName [, Boolean])

example

el.addEventListener('blur', checkUsername, false )

Bassically you get a DOM node and then attach a event listener to that node you have the event and attach one of the functions the third argument is called capture and is ussually set to false

<input type="text" id="username" onblur="checkUsername()">
function fns(){
// function code to run when event is triggered 
}


// get element 
const el = document.getElementById('id')

// attach handler method 
el.onblur = fns // attach function 

// attach listener method
el.addListerer('blur', fns, false)
// we can use anonymous functions to pass function arguments
usernameInput.addEventListener('blur',  () => {
   checkUsername(3)
})

Event Flow

Event Bubbling default
graph BT; a((< a >)) --> li li(< li >) --> ul ul(< ul >) --> body body(< body >) --> html html(< html >) --> Document classDef green stroke:SpringGreen, stroke-width: 1px classDef el fill:green class a,li,ul,body,html,Document green class a el
Event Capturing IE8
graph TD; Document --> html(< html >) html --> body(< body >) li --> a((< a >)) ul --> li(< li >) body --> ul(< ul >) classDef red stroke:firebrick, stroke-width: 1px classDef el fill:indianred, color:black class a,li,ul,body,html,Document red class a el

Event Object

When an event occurs, the event tells you information about the event and the element it happened upon

function checkUsername(e, minLength) {
  e.preventdefault() // prevent the default behaviour of going somewhere else
  e.stopPropagation() // prevent the bubble event propagation

  /* Properties of e */
  e.type // type of event being fired
  e.target // target of event (the element)
  e.cancelable // wherther you can cancel default behaviour of element
}

// this is where we get the event object "e"
el.addEventListener('blur', (e) => checkUsername(e, 5))
graph TB; parent>get <code>< ul ></code> element for shopping list] --> addevent{is<br><code>addEventListener</code><br>supported} addevent -- Yes --> y[use <code>attachEventListener</code>] addevent -- No --> n[use <code>attachEvent</code>] y --> event(<b>Event <code>click</code> on any link in the list</b>) n --> event event --> a subgraph Function:<code>itemDone</code>Removes an item when completed a[<b>Create Variables:</b><br><code>target</code>: element that was clicked]--> b[Get element clicked Call <code>getTarget</code>] b-->e>remove <code>< li ></code> from <code>< ul ></code>] e-->f{is <code>preventDefault</code><br>Supported} f--Y-->preventDefault f--N-->returnValue end
graph TB subgraph Function:<code>getTarget</code>Get element that user clicked f{is there no<br><b>Event Object</b> }--Y-->a[Get target of event] f--N-->b[Get target of event] end
/* Event Scripts (of above diagram) */

const usernameInput = document.getElementById('username')
// usernameInput.onblur = checkUsername // traditional event handler
usernameInput.addEventListener('blur', () => {
  return checkUsername(3)
}) // event listener method

function checkUsername(maxlength) {
  const elMsg = document.getElementById('feedback')
  const elUsername = document.getElementById('username')
  // @ts-ignore // HTMLINPUT TYPE BREAKS CODE
  const userLength = elUsername.value.length
  elMsg.textContent =
    userLength < maxlength
      ? `Username must be ${maxlength} characters or more\nYou need ${
          maxlength - userLength
        } more`
      : ''
}

/** EVENT FLOW **/
// Set up event listeners to call itemDone() on click
const shoppingList = document.getElementById('shoppingList')
addClickEvent(shoppingList)

function addClickEvent(el) {
  el.addEventListener ? defaultAddListenerMethod(el) : oldAddListenerMethod(el)

  function defaultAddListenerMethod(el) {
    el.addEventListener('click',  (e) => {
      // e.stopPropagation() // note important to prevent bubbling causing link to go elsewhere
      itemDone(e)
    })
  }

  /** For Old IE method of calling itemDone */
  function oldAddListenerMethod(el) {
    el.attachEvent('onclick', (e) => itemDone(e))
  }
}

function getTarget(e) {
  // For old IE event object
  if (!e) e = window.event

  // Get the target of event
  return e.target || e.srcElement
}

function itemDone(e) {
  const target = getTarget(e)

  if (target.nodeName.toLowerCase() == 'a') {
    const elListItem = target.parentNode
    const elList = elListItem.parentNode
    elList.removeChild(elListItem)
  }
  if (target.nodeName.toLowerCase() == 'em') {
    const elListItem = target.parentNode.parentNode
    const elList = elListItem.parentNode
    elList.removeChild(elListItem)
  }
  /* Prevent the link from taking you elsewhere if preventDefault() works use preventDefault() otherwise Use old IE version similiar for stopping event propagation */
  e.stopPropagation ? e.stopPropagation() : (e.cancelBubble = true)
  e.preventDefault ? e.preventDefault() : (e.returnValue = false)
}

Form Events

submit

When a form is submitted, this event fires on the node representing the form element

Ussually used when user is ready to send form data to server

change

Fires when a one of several form elements changes eg

input

Used on <input> or <textarea> elements

Mutation Observer

The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature
const observer = new MutationObserver(() => {
  console.log("There was a DOM change detected");
  observer.disconnect();
})

observer.observe(targetNode, { attributes: true, childList: true, subtree: true } as config)

HTML5 events

DOMContentLoaded

This event fires when the DOM tree is loaded but images, CSS, Javascript may turn out to still be loading

It can be attached to the window or document object

Note: Since the load event for scripts could be fired after above event this means that the html inserted by script might not have finished yet

    window.addEventListener('DOMContentLoaded',
      () => textInput.focus() as callback
      , false)

beforeunload

This event should only be used to tell the user that form data will not be saved, thus browsers make sure that this cannot be abused for anything else such as telling user to not redirect from page

ask before redirect
    window.addEventListener('beforeunload', (event) => {
      // for firefox this is the only way to activate this event
      event.preventDefault()

      /* Alternative way to activate event for other browsers */
      // const message = 'You have changes that have not been saved... '
      // (event || window.event).returnValue = message
      // return message
    })

There are other events for mobile and some not listed here